package br.com.rhinovet.converter;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.persistence.Id;

/**
 * 	Converter para entidades. <br /> 
 * 	Baseia-se nas anotações @Id e @EmbeddedId para identificar o <br /> 
 * 	atributo que representa o id da entidade. <br />
 * 	Capaz de detectar as anotações nas classes superiores. <br />
 * 
 * OBS.: Para que o converter funcione corretamente (Valor selecionado), <br /> 
 * é necessário implementar o equasl e HashCode na entidade.
 * @author aaconsolaro
 *
 */
@FacesConverter(value = "converterGeral")
public class ConverterGeral implements Converter {
	public Object getAsObject(FacesContext ctx, UIComponent componente,  
			String valor) {  
		if (valor != null) {  
			return componente.getAttributes().get(valor);  
		}  
		return null;  
	}  

	//TODO - Allan - VERIFICAR A MELHOR FORMA DE ENVIAR A MENSAGEM PARA O SUAURIO, CASO DÊ ALGUM ERRO. 
	public String getAsString(FacesContext ctx, UIComponent componente,  
			Object objeto) {  
		if (objeto != null && !(objeto instanceof String)) {  
		try {  
			String id = this.getId(getClass(ctx, componente), objeto);  

				if (id == null) {  
					id = "";
				}  
				id = id.trim();  
				componente.getAttributes().put(id,  
						getClass(ctx, componente).cast(objeto));  
				return id; 
		} catch (SecurityException e) {  
			e.printStackTrace();  
		} catch (IllegalArgumentException e) {  
			e.printStackTrace();  
		} catch (NoSuchFieldException e) {  
			e.printStackTrace();  
		} catch (IllegalAccessException e) {  
			e.printStackTrace();  
		}
	}
		return null;  
	}  

	/** 
	 * Obtém a classe do objeto. 
	 * 
	 * @author aaconsolaro
	 * @param FacesContext facesContext 
	 * @param UICompoment compoment 
	 * @return  Class<?> 
	 */  
	private Class<?> getClass(FacesContext facesContext, UIComponent component) {  
		return component.getValueExpression("value").getType(  
				facesContext.getELContext());  
	}  


	/** 
	 * Retorna a representação em String do retorno do método anotado com @Id ou @EmbeddedId do objeto. 
	 * 
	 * @author aaconsolaro
	 * @param Class<?> clazz 
	 * @return  String 
	 */  
	public String getId(Class<?> clazz, Object obj) throws SecurityException,  
	NoSuchFieldException, IllegalArgumentException,  
	IllegalAccessException {  

		List<Class<?>> hierarquiaClasses = this.getHierarquiaClasses(clazz);  

		for (Class<?> classeHierarquia : hierarquiaClasses) {  
			for (Field field : classeHierarquia.getDeclaredFields()) {  
				if ((field.getAnnotation(Id.class)) != null  
						/*|| field.getAnnotation(EmbeddedId.class) != null*/) {  
					Field privateField = classeHierarquia  
							.getDeclaredField(field.getName());  
					privateField.setAccessible(true);  
					if (privateField.get(clazz.cast(obj)) != null) {  

						return (String) field.getType()  
								.cast(privateField.get(clazz.cast(obj)))  
								.toString();  
					}  
				}  
			}  
		}  
		return null;  
	}  

	/** 
	 * Retorna uma lista com a hierarquia de classes, sem considerar a classe Object.class 
	 * 
	 * @param Class<?> clazz 
	 * @author aaconsolaro
	 * @return  List<Class<?>> clazz 
	 */  
	public List<Class<?>> getHierarquiaClasses(Class<?> clazz) {  

		List<Class<?>> hierarquiaClasses = new ArrayList<Class<?>>();  
		Class<?> classeHierarquia = clazz;  
		while(classeHierarquia != Object.class) {  
			hierarquiaClasses.add(classeHierarquia);  
			classeHierarquia = classeHierarquia.getSuperclass();  

		}  
		return hierarquiaClasses;  
	}   
}  